
package cn.iocoder.yudao.module.trade.cart.controller.app;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.api.dine.DineConfigApi;
import cn.iocoder.yudao.module.api.dine.enums.DineAttributeEnum;
import cn.iocoder.yudao.module.api.dine.vo.DineConfigVO;
import cn.iocoder.yudao.module.api.infrastructure.config.ConfigApiV2;
import cn.iocoder.yudao.module.api.infrastructure.config.dto.MemberConfigRespDTO;
import cn.iocoder.yudao.module.api.member.level.MemberLevelApi;
import cn.iocoder.yudao.module.api.product.sku.ProductSkuApi;
import cn.iocoder.yudao.module.api.product.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.api.product.spu.ProductSpuApi;
import cn.iocoder.yudao.module.api.product.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.api.product.spu.enums.ProductPriceModeEnum;
import cn.iocoder.yudao.module.api.shop.info.ShopInfoApi;
import cn.iocoder.yudao.module.api.shop.info.dto.ShopInfoDTO;
import cn.iocoder.yudao.module.trade.cart.convert.TradeCartConvert;
import cn.iocoder.yudao.module.trade.cart.dal.dataobject.CartDO;
import cn.iocoder.yudao.module.trade.cart.service.CartService;
import cn.iocoder.yudao.module.trade.cart.vo.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.api.infrastructure.ConfigConstants.CONFIG_MEMBER;
import static java.util.Collections.emptyList;

@Tag(name = "用户 App - 购物车")
@RestController
@RequestMapping("/trade/cart")
@RequiredArgsConstructor
@Validated
@Slf4j
public class AppCartController {

    @Resource
    private CartService cartService;

    @Resource
    private ProductSpuApi productSpuApi;
    @Resource
    private ProductSkuApi productSkuApi;
    @Resource
    private ShopInfoApi shopInfoApi;
    @Resource
    private ConfigApiV2 configApiV2;
    @Resource
    private MemberLevelApi memberLevelApi;
    @Resource
    private DineConfigApi dineConfigApi;

    @PostMapping("/add")
    @Operation(summary = "添加购物车产品")
    public CommonResult<Long> addCart(@Valid @RequestBody AppCartAddReqVO addCountReqVO) {
        return success(cartService.addCart(getLoginUserId(), addCountReqVO));
    }

    @PostMapping("/update-count")
    @Operation(summary = "更新购物车产品数量", description = "数量为 0 时将自动从购物车删除")
    public CommonResult<Boolean> updateCartCount(@Valid @RequestBody AppCartUpdateCountReqVO updateReqVO) {
        cartService.updateCartCount(getLoginUserId(), updateReqVO);
        return success(true);
    }

    @PostMapping("/update-selected")
    @Operation(summary = "更新购物车产品选中")
    public CommonResult<Boolean> updateCartSelected(@Valid @RequestBody AppCartUpdateSelectedReqVO updateReqVO) {
        cartService.updateCartSelected(getLoginUserId(), updateReqVO);
        return success(true);
    }

    @PostMapping("/reset")
    @Operation(summary = "重置购物车产品")
    public CommonResult<Boolean> resetCart(@Valid @RequestBody AppCartResetReqVO updateReqVO) {
        cartService.resetCart(getLoginUserId(), updateReqVO);
        return success(true);
    }

    @GetMapping("/delete")
    @Operation(summary = "删除购物车产品（按购物车编号）")
    @Parameter(name = "ids", description = "购物车编号，多个之间用英文逗号隔开", required = true, example = "1024,2048")
    public CommonResult<Boolean> deleteCart(@RequestParam("ids") List<Long> ids) {
        cartService.deleteCart(getLoginUserId(), ids);
        return success(true);
    }

    @GetMapping("/deleteByShopId")
    @Operation(summary = "删除购物车产品（按店铺编号）", description = "相当于按店铺清空购物车")
    @Parameter(name = "shopId", description = "店铺编号", example = "1")
    public CommonResult<Boolean> deleteCart(@RequestParam("shopId") Long shopId) {
        cartService.deleteCart(getLoginUserId(), shopId);
        return success(true);
    }

    @GetMapping("get-count")
    @Operation(summary = "获取购物车中的产品数量", description = "小程序主页有购物车列表，故不登录的情况下不强制报错提示")
    @Parameter(name = "shopId", description = "店铺编号", example = "1")
    @PermitAll
    public CommonResult<Integer> getCartCount(@RequestParam(value = "shopId", required = false) Long shopId) {
        Long userId = getLoginUserId();
        if (userId == null) {
            return success(0);
        }
        return success(cartService.getCartCount(userId, shopId));
    }

    @GetMapping("/list")
    @Operation(summary = "获取购物车列表")
    @Parameter(name = "shopId", description = "店铺编号。若不传店铺编号，则只获取商城产品类的，排除美食、家政等类别的", example = "1")
    @PermitAll
    public CommonResult<List<AppCartListRespVO>> getCartList(@RequestParam(value = "shopId", required = false) Long shopId) {
        Long userId = getLoginUserId();
        if (userId == null) {
            return success(List.of());
        }

        List<CartDO> carts = cartService.getCartList(userId, shopId);
        // 如果未空，则返回空结果
        if (CollUtil.isEmpty(carts)) {
            return success(emptyList());
        }
        // 查询店铺、SPU、SKU 列表
        Set<Long> shopIds = convertSet(carts, CartDO::getShopId);
        Map<Long, ShopInfoDTO> shops = shopInfoApi.getShopInfoMap(shopIds);
        List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(carts, CartDO::getSpuId));
        List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(carts, CartDO::getSkuId));
        // 如果 SPU 被删除，则删除购物车对应的产品。延迟删除
        // 为什么不是 SKU 被删除呢？因为 SKU 被删除时，还可以通过 SPU 选择其它 SKU
        cartService.deleteCartIfSpuDeleted(carts, spus);
        // 拼接数据

        int deductUnitPrice = 0;
        MemberConfigRespDTO config = configApiV2.getConfig(CONFIG_MEMBER, MemberConfigRespDTO.class);
        if (!config.isPointDeductDisable()) {
            deductUnitPrice = config.getPointTradeDeductUnitPrice();
        }
        int discountPercent = memberLevelApi.getMemberLevelDiscountPercentByUserId(userId);
        List<AppCartItemRespVO> cartList = TradeCartConvert.INSTANCE.convertList(carts, shops, spus, skus, deductUnitPrice, discountPercent);
        // 适配称重计价模式
        Map<Long, DineConfigVO> dineConfigs = dineConfigApi.getDetailMap(shopIds);
        for (AppCartItemRespVO cartItem : cartList) {
            if (Objects.equals(cartItem.getSpu().getPriceMode(), ProductPriceModeEnum.TOTAL_WEIGHT.getValue())) {
                DineConfigVO dineConfigVO = dineConfigs.get(cartItem.getSpu().getShopId());
                if (dineConfigVO != null) {
                    cartItem.getSpu().setUnitName(dineConfigVO.getWeightUnitName());
                    int unitPrice = dineConfigVO.getVegetableUnitPrice();
                    if (Objects.equals(cartItem.getSpu().getDishAttribute(), DineAttributeEnum.MEAT.getValue())) {
                        unitPrice = dineConfigVO.getMeatUnitPrice();
                    }
                    cartItem.getSpu().setPrice(cartItem.getSpu().getStepQuantity() * unitPrice);
                    cartItem.getSpu().setVipPrice(cartItem.getSpu().getPrice());
                    cartItem.getSpu().setCostPrice(0);
                    cartItem.getSku().setPrice(cartItem.getSpu().getStepQuantity() * unitPrice);
                    cartItem.getSku().setVipPrice(cartItem.getSpu().getPrice());
                    cartItem.getSku().setCostPrice(0);
                }
            }
        }

        // 按店铺分组
        Map<Long, List<AppCartItemRespVO>> cartsByShop = new HashMap<>();
        cartList.forEach(cart -> cartsByShop.computeIfAbsent(cart.getShopId(), k -> new ArrayList<>()).add(cart));
        // 转换数据格式
        List<AppCartListRespVO> respVOS = new ArrayList<>();
        for (Map.Entry<Long, List<AppCartItemRespVO>> entry : cartsByShop.entrySet()) {
            AppCartListRespVO respVO = new AppCartListRespVO();
            respVO.setShopId(entry.getKey());
            respVO.setShop(shops.get(entry.getKey()));
            respVO.setItems(entry.getValue());
            respVOS.add(respVO);
        }
        return success(respVOS);
    }


}